﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using CueMounter.DokanStructure;
using Dokan;
using CueMounter.DokanStructure.RamDisk;

namespace CueMounter {
	public partial class MainForm : Form {
		private object syncRoot = new object();
		private string CurrentFile = null;
		private DokanMounter Mounter;
		private char DriveLetter = 'X';

		public MainForm() {
			InitializeComponent();

			Mounter = new DokanMounter();
			//Mounter.Mount(new DokanMountOptions('X', new DokanDirectory[] { new DokanMirrorDirectory(@"C:\Work") }));
			PopulateDriveLetters();
		}

		private void PopulateDriveLetters() {
			string[] drives = Directory.GetLogicalDrives();
			for(char c = 'A'; c <= 'Z'; ++c) {
				if(drives.Contains(c + ":\\")) {
					continue;
				}
				comboDriveLetter.Items.Add(c + ":");
			}
			if(drives.Contains(DriveLetter + ":\\")) {
				DriveLetter = ((string)comboDriveLetter.Items[comboDriveLetter.Items.Count - 1])[0];
				comboDriveLetter.SelectedIndex = comboDriveLetter.Items.Count - 1;
			} else {
				comboDriveLetter.SelectedItem = DriveLetter + ":";
			}
		}

		private void MainForm_Load(object sender, EventArgs e) {
			SetStatusTextSafe(MessageStrings.DropToMount);

			try {
				DokanNet.DokanVersion();
			} catch {
				MessageBox.Show(MessageStrings.DokanNotFound, "Dokan Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error);
				Close();
			}
		}

		private void SetStatusTextSafe(string text) {
			Invoke(new Action(delegate {
				labelStatus.Text = text;
			}));
		}

		private char SelectedDriveLetter {
			get {
				return ((string)comboDriveLetter.SelectedItem)[0];
			}
		}

		private void SetMountedStatusSafe(bool isMounted) {
			Invoke(new Action(delegate {
				btnUnmount.Enabled = isMounted;
				btnAuCDTect.Enabled = isMounted;
			}));
		}

		private void StartThreadWithSync(Action action, Action failAction = null, bool messageErrors = true) {
			new Thread(delegate() {
				if(Monitor.TryEnter(syncRoot)) {
					try {
						action();
					} finally {
						Monitor.Exit(syncRoot);
					}
				} else {
					if(failAction != null) {
						failAction();
					}
					if(messageErrors) {
						MessageBox.Show(MessageStrings.PleaseWait);
					}
				}
			}).Start();
		}

		private void Mount(string file) {
			if(Mounter.IsMounted) {
				throw new InvalidOperationException("Can't mount again");
			}

			string friendlyName = Path.GetFileName(file);
			SetStatusTextSafe(MessageStrings.Initializing[friendlyName]);

			DokanDirectory fileProvider = null;
			try {
				fileProvider = DokanDirectoryFactory.GetFileProvider(file);
			} catch(Exception e) {
				SetStatusTextSafe(MessageStrings.ErrorInitializing[friendlyName, e.Message]);
				return;
			}

			SetStatusTextSafe(MessageStrings.Mounted[friendlyName]);
			CurrentFile = file;

			SetMountedStatusSafe(true);

			DokanDirectory[] fileProviders = new DokanDirectory[] {
					fileProvider,
					new DokanDirectoryContainer(new DokanMirrorDirectory(Path.GetDirectoryName(file), "dst")),
					new DokanDirectoryContainer(new DokanRamDiskDirectory("ramdisk"))
				};
			Mounter.Mount(new DokanMountOptions(DriveLetter, fileProviders));
		}

		private void Unmount() {
			if(!Mounter.IsMounted) {
				throw new InvalidOperationException("Can't unmount: not mounted");
			}
			SetStatusTextSafe(MessageStrings.Unmounting);
			SetMountedStatusSafe(false);
			foreach(var fileProvider in Mounter.CurrentOptions.RootDirectories) {
				fileProvider.Dispose();
			}
			CurrentFile = null;
			Mounter.Unmount();
		}

		private void Remount() {
			if(!Mounter.IsMounted) {
				throw new InvalidOperationException("Can't unmount: not mounted");
			}
			SetStatusTextSafe(MessageStrings.Unmounting);
			SetMountedStatusSafe(false);
			Mounter.Unmount();
			Mount(CurrentFile);
		}

		private void comboDriveLetter_SelectedIndexChanged(object sender, EventArgs e) {
			if(SelectedDriveLetter == DriveLetter) {
				return;
			}
			StartThreadWithSync(
				delegate {
					DriveLetter = SelectedDriveLetter;
					if(Mounter.IsMounted) {
						Remount();
					}
				},
				delegate {
					comboDriveLetter.SelectedItem = DriveLetter + ":";
				});
		}

		private void btnUnmount_Click(object sender, EventArgs e) {
			StartThreadWithSync(delegate {
				Unmount();
				SetStatusTextSafe(MessageStrings.DropToMount);
			});
		}

		private bool ValidateDrop(IDataObject data, out string fileName, out string errorMessage) {
			if(!data.GetDataPresent(DataFormats.FileDrop)) {
				fileName = null;
				errorMessage = "Not a file drop";
				return false;
			}
			string[] files = (string[])data.GetData(DataFormats.FileDrop);
			if(files.Length != 1) {
				fileName = null;
				errorMessage = MessageStrings.OnlyOneFile;
				return false;
			}
			string file = files[0];
			if(!DokanDirectoryFactory.CanGetFileProvider(file)) {
				fileName = null;
				errorMessage = MessageStrings.UnsupportedFile;
				return false;
			}
			fileName = file;
			errorMessage = "";
			return true;
		}

		private void labelStatus_DragEnter(object sender, DragEventArgs e) {
			string fileName, errorMessage;
			if(ValidateDrop(e.Data, out fileName, out errorMessage)) {
				e.Effect = DragDropEffects.All;
			}
		}

		private void labelStatus_DragDrop(object sender, DragEventArgs e) {
			string fileName, errorMessage;
			if(ValidateDrop(e.Data, out fileName, out errorMessage)) {
				StartThreadWithSync(delegate {
					if(Mounter.IsMounted) {
						Unmount();
					}
					Mount(fileName);
				});
			} else {
				MessageBox.Show(errorMessage);
			}
		}

		private void CloseSafe() {
			Invoke(new Action(delegate {
				Close();
			}));
		}

		private bool closePending = false;
		private bool closeOk = false;
		private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
			if(closeOk) {
				return;
			}
			e.Cancel = true;

			new Thread(delegate() {
				lock(syncRoot) {
					if(closePending) {
						return;
					}
					closePending = true;

					if(Mounter.IsMounted) {
						Unmount();
					}
					closeOk = true;
					CloseSafe();
				}
			}).Start();
		}

		private void btnOpenDrive_Click(object sender, EventArgs e) {
			try {
				Process.Start(SelectedDriveLetter + ":\\");
			} catch {
				MessageBox.Show(MessageStrings.DriveNotFound);
			}
		}

		private void btnAuCDTect_Click(object sender, EventArgs e) {
			try {
				string[] files = Directory.GetFiles(SelectedDriveLetter + ":\\", "*.wav");
				new AuCDTectForm(files).Show(this);
			} catch(Exception ex) {
				MessageBox.Show("Error running AuCDTect: " + ex.Message);
			}
		}
	}
}
